﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using InterLinq.Base;

namespace InterLinq.Objects {
	/// <summary>
	/// LINQ to Objects specific implementation of the
	/// <see cref="IQueryHandler"/>.
	/// </summary>
	/// <seealso cref="IQueryHandler"/>
	public class ObjectQueryHandler : IQueryHandler, IBackendProvider {
		#region Fields

		private readonly IObjectSource m_objectSource;

		#endregion

		#region Constructors

		/// <summary>
		/// Initializes this class.
		/// </summary>
		/// <param name="objectSource"><see cref="IObjectSource"/> used get all objects of a type.</param>
		public ObjectQueryHandler(IObjectSource objectSource) {
			if (objectSource == null) {
				throw new ArgumentNullException("objectSource");
			}
			m_objectSource = objectSource;
		}

		#endregion

		private readonly List<Type> m_Types = new List<Type>();

		#region IBackendProvider Members

		public object BeginTransaction() {
			m_objectSource.Lock.AcquireWriterLock(-1);
			return null;
		}

		public void CommitTransaction(object transaction) {
			m_objectSource.Lock.ReleaseWriterLock();
		}

		public void SaveChanges(IList<BackendProviderChangeSet> Changes) {
			//nothing to do as we don't have a back end;
			return;
		}

		public void SaveChanges(IList<BackendProviderDeleteSet> Changes) {
			foreach (BackendProviderDeleteSet change in Changes) {
				m_objectSource.DeleteObject(change.type, change.obj);
			}
		}

		public void SaveChanges(IList<BackendProviderNewSet> Changes) {
			foreach (BackendProviderNewSet change in Changes) {
				//var mi = m_objectSource.GetType().GetMethod("AddObject", new Type[] { change.type }).MakeGenericMethod(change.type);
				//mi.Invoke(m_objectSource, new object[] { change.obj });
				m_objectSource.AddObject(change.type, change.obj);
			}
		}

		#endregion

		#region IQueryHandler Members

		public ReaderWriterLock LockOfDataSource {
			get { return m_objectSource.Lock; }
		}

		public IEnumerable<Type> GetTableTypes() {
			return m_Types;
		}

		/// <summary>
		/// Returns an <see cref="IQueryable{T}"/>.
		/// </summary>
		/// <typeparam name="T">Generic Argument of the returned <see cref="IQueryable{T}"/>.</typeparam>
		/// <returns>Returns an <see cref="IQueryable{T}"/>.</returns>
		/// <seealso cref="IQueryHandler.GetTable{T}"/>
		public IQueryable<T> GetTable<T>() where T : class {
			if (!m_Types.Contains(typeof(T))) {
				m_Types.Add(typeof(T));
			}
			return m_objectSource.GetObjects<T>().AsQueryable();
		}

		/// <summary>
		/// Returns an <see cref="IQueryable{T}"/>.
		/// </summary>
		/// <param name="type">Type of the returned <see cref="IQueryable{T}"/>.</param>
		/// <returns>Returns an <see cref="IQueryable{T}"/>.</returns>
		/// <seealso cref="IQueryHandler.GetTable"/>
		public IQueryable GetTable(Type type) {
			if (!m_Types.Contains(type)) {
				m_Types.Add(type);
			}
			MethodInfo getTableMethod = GetType().GetMethod("GetTable", BindingFlags.Instance | BindingFlags.Public, null,
															  new Type[0], null);
			MethodInfo genericGetTableMethod = getTableMethod.MakeGenericMethod(type);
			return (IQueryable)genericGetTableMethod.Invoke(this, new object[0]);
		}

		/// <summary>
		/// Tells the <see cref="IQueryHandler"/> to start a new the session.
		/// </summary>
		/// <returns>True, if the session creation was successful. False, if not.</returns>
		/// <seealso cref="IQueryHandler.StartSession"/>
		public bool StartSession() {
			return true;
		}

		/// <summary>
		/// Tells the <see cref="IQueryHandler"/> to close the current session.
		/// </summary>
		/// <returns>True, if the session closing was successful. False, if not.</returns>
		/// <seealso cref="IQueryHandler.CloseSession"/>
		public bool CloseSession() {
			return true;
		}

		#endregion
	}
}